package com.br.aiops.storage.counter;

import com.alibaba.fastjson.JSONObject;
import com.br.aiops.counter.AlertLogCounter;
import com.br.aiops.counter.Counter;
import com.br.aiops.counter.CounterLadderValue;
import com.br.aiops.cycle.Cycle;
import com.br.aiops.listeners.ladder.LadderCycleStatus;
import com.br.aiops.listeners.ladder.LadderCycleStatusCache;
import com.br.aiops.rule.Rule;
import com.br.aiops.schedule.AbstractSchedule;
import com.br.aiops.schedule.ScheduleCycle;
import com.br.aiops.schedule.Schedules;
import com.br.aiops.utils.Constants;
import com.googlecode.aviator.AviatorEvaluator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class LoadCounterService {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoadCounterService.class);

    @Autowired
    private CounterRepository counterRepository;

    @Autowired
    private CounterScheduleStatusRepository counterScheduleStatusRepository;

    @Autowired
    private CounterLadderStatusRepository counterLadderStatusRepository;

    @Autowired
    private CounterLadderValueRepository counterLadderValueRepository;

    @Autowired
    private Counter counter;

    @Autowired
    private Schedules schedules;

    public void loadCounterByPartition(int partition) {
        List<AlertLogCounter> alertLogCounters = counterRepository.findByPartition(partition).stream()
                .map(counterDTO -> {
                    AlertLogCounter counter = JSONObject.parseObject(counterDTO.getValue(), AlertLogCounter.class);
                    Rule rule = counter.getRule();
                    //表达式引擎不能反序列化，所以单独初始化
                    if (StringUtils.isNotBlank(rule.getExpr())) {
                        rule.setExpression(AviatorEvaluator.compile(rule.getExpr()));
                    }
                    return counter;
                }).collect(Collectors.toList());

        alertLogCounters.forEach(alertLogCounter -> {
            //保存全局counter
            counter.getCounter().put(alertLogCounter.counterKey(), alertLogCounter);

            if (alertLogCounter.shouldLadderAlert()) {
                //加载ladder status
                loadLadderStatus(alertLogCounter);
                //加载ladder value
                loadLadderValue(alertLogCounter);
            } else {
                //加载schedule status
                loadScheduleStatus(alertLogCounter);
            }
        });
    }

    private void loadLadderValue(AlertLogCounter alertLogCounter) {
        CounterLadderValueDTO ladderValueDTO = counterLadderValueRepository.findLadderValue(alertLogCounter.counterKey());
        if (Objects.nonNull(ladderValueDTO)) {
            CounterLadderValue.LadderValue ladderValue = JSONObject.parseObject(ladderValueDTO.getLadderValue(), CounterLadderValue.LadderValue.class);
            CounterLadderValue.setLadderValue(ladderValueDTO.getCounterKey(), ladderValue);
        }
    }

    private void loadScheduleStatus(AlertLogCounter alertLogCounter) {
        CounterScheduleStatusDTO statusDTO = counterScheduleStatusRepository.findScheduleStatus(alertLogCounter.counterKey());
        AbstractSchedule.Status loadCurrentStatus = JSONObject.parseObject(statusDTO.getCurrentStatus(), AbstractSchedule.Status.class);
        AbstractSchedule.Status loadPrevStatus = JSONObject.parseObject(statusDTO.getPrevStatus(), AbstractSchedule.Status.class);
        ScheduleCycle loadScheduleCycle = JSONObject.parseObject(statusDTO.getScheduleCycle(), ScheduleCycle.class);

        AbstractSchedule schedule = getSchedule(alertLogCounter.getRule().getDuration());
        ScheduleCycle systemScheduleCycle = schedule.getCycle();
        /**
         *   只有三种情况：
         *   1、load的prev=system prev，当前周期内加载，此时加载current status和prev status；
         *   2、load current = system prev，过了一个周期，当前周期变为上一周期，此时将current status加载到prev status；
         *   3、其余情况则认为已经错过周期，不加载；
         */
        String counterKey = alertLogCounter.counterKey();
        if (equalsCycle(loadScheduleCycle.getPrevCycle(), systemScheduleCycle.getPrevCycle())) {
            schedule.getCurrentCountStatus().put(counterKey, loadCurrentStatus);
            schedule.getPrevCountStatus().put(counterKey, loadPrevStatus);
        } else if (equalsCycle(loadScheduleCycle.getCurrentCycle(), systemScheduleCycle.getPrevCycle())) {
            schedule.getPrevCountStatus().put(counterKey, loadCurrentStatus);
        } else {
            LOGGER.warn("加载的数据已经错过近两周期，将不进行加载。加载的数据为：{}", statusDTO);
        }
    }

    private boolean equalsCycle(Cycle source, Cycle target) {
        if (Objects.isNull(source) || Objects.isNull(target)) {
            return false;
        }
        return source.getStart() == target.getStart() && source.getEnd() == target.getEnd();
    }

    private void loadLadderStatus(AlertLogCounter alertLogCounter) {
        /**
         *次数的不在阶梯周期中，周期包含的是时间；次数在全局计数中存储
         */
        if (alertLogCounter.getRule().getLadderType() == Constants.LADDER_TYPE_TIMES) {
            return;
        }
        String counterKey = alertLogCounter.counterKey();
        CounterLadderStatusDTO statusDTO = counterLadderStatusRepository.findLadderStatus(counterKey);
        LadderCycleStatus ladderCycleStatus = JSONObject.parseObject(statusDTO.getLadderCycleStatus(), LadderCycleStatus.class);
        LadderCycleStatusCache.getLadderCycleStatus().put(counterKey, ladderCycleStatus);
    }


    private AbstractSchedule getSchedule(int duration) {
        return schedules.getSchedule(duration);
    }
}
